
(function ($) {
    // 根据容器宽度生成拼图
    class Builder {
        static defaults = {
            levelNumber: 2, // 方块个数levelNumber*levelNumber
            image: 'images/01.jpg', // 图片
            dragDuration: 500, // 拖动动画时长
            createDuration: 500, // 生成拼图时长
            startBtn: null, // 开始按钮/无按钮时，初始化直接生成拼图
            callback: null // 拼图成功回调
        }
        
        rectangle = 0; // 矩形宽度
        originArray = []; // 改变位置前图片位置数组
        imageArray = [];  // 改变位置前图片元素数组
        zIndex = 1; // 图层

        constructor(ele, options) {
            this.$ele = ele;
            this.opts = $.extend({}, Builder.defaults, options);
        }
        init() {
            this.rectangle = parseInt(this.$ele.css('width'));
            let $ulBox = $('<ul id="pt_box" style="position:relative;margin:0;padding:0;"></ul>');
            let img = new Image();
            img.src = this.opts.image;
            img.onload = function () {
                $ulBox.css({ 'width': this.rectangle, 'height': this.rectangle });
                let itemRectangle = Math.floor(this.rectangle / this.opts.levelNumber); // 子矩形宽度
                for (let i = 0; i < this.opts.levelNumber; i++) {
                    for (let j = 0; j < this.opts.levelNumber; j++) {
                        let top = i * itemRectangle;
                        let left = j * itemRectangle;
                        let index = i * this.opts.levelNumber + j;

                        let li = `<li 
                                    id="pt_game${index}"
                                    ismove="false"
                                    style="position: absolute;
                                    top: ${top}px;
                                    left:${left}px;
                                    list-style: none;
                                    cursor: move;
                                    width:${itemRectangle}px;
                                    height:${itemRectangle}px;
                                    background-image:url(${this.opts.image});
                                    background-size:${this.rectangle}px ${this.rectangle}px;
                                    background-position: ${-left}px ${-top}px"
                                ></li>`;

                        let $li = $(li);
                        this.originArray.push([index, left, top]);
                        this.imageArray.push($li[0]);

                        $li[0].onmousedown = function (event) {
                            let that = this;
                            let dragTarget = event.target;
                            let isMouseMove = false;
                            // 距离拖动元素最近元素
                            let nearestTarget;
                            // 拖动对象初始位置
                            let dragOriginLeft = parseInt(dragTarget.style.left);
                            let dragOriginTop = parseInt(dragTarget.style.top);
                            // 鼠标相对当前图片位置
                            let posX = event.clientX - dragTarget.offsetLeft;
                            let posY = event.clientY - dragTarget.offsetTop;
                            that.zIndex++;
                            // 移动中图片不绑定事件
                            if (!Boolean(dragTarget.isMove)) {
                                document.onmousemove = function (event) {
                                    // 当前图片左边顶边位置
                                    let realLeft = event.clientX - posX;
                                    let realTop = event.clientY - posY;

                                    // 最小极限位置
                                    if (realLeft < 0) {
                                        realLeft = 0;
                                    }
                                    if (realTop < 0) {
                                        realTop = 0;
                                    }
                                    // 最大极限位置
                                    if (realLeft > $ulBox[0].offsetWidth - dragTarget.offsetWidth) {
                                        realLeft = $ulBox[0].offsetWidth - dragTarget.offsetWidth;
                                    }
                                    if (realTop > $ulBox[0].offsetHeight - dragTarget.offsetHeight) {
                                        realTop = $ulBox[0].offsetHeight - dragTarget.offsetHeight;
                                    }

                                    $(dragTarget).css({ 'left': realLeft, 'top': realTop, 'z-index': that.zIndex });
                                    // 获取离拖动元素最近的元素
                                    nearestTarget = that.getNearest(dragTarget);
                                    isMouseMove = true;
                                    return false;
                                };
                                document.onmouseup = function () {
                                    document.onmousemove = null;
                                    document.onmouseup = null;
                                    // 判断是否是一个完整的拖拽动作
                                    if (isMouseMove === true) {
                                        nearestTarget.style.zIndex = that.zIndex;
                                        that.setOpacity(nearestTarget, 100);
                                        that.startMove(nearestTarget, dragOriginLeft, dragOriginTop, that.opts.dragDuration);
                                        that.startMove(dragTarget, parseInt(nearestTarget.style.left), parseInt(nearestTarget.style.top), that.opts.dragDuration, 1);
                                    }
                                    isMouseMove = false;
                                    dragTarget = null;
                                    return false;
                                };
                            }
                            return false;
                        }.bind(this);
                        $ulBox.append($li);
                    }
                }
                this.$ele.append($ulBox);
                if (!this.opts.startBtn) {
                    this.rank();
                } else {
                    let that = this;
                    that.opts.startBtn.off();
                    that.opts.startBtn.click(function () {
                        that.rank();
                    })
                }
            }.bind(this);
        }
        // 获取离拖动元素最近的元素
        getNearest(dragTarget) {
            // 存储与每个对象的最小距离的数组
            let disArray = [];
            let imageNearArray = $.extend(true, [], this.imageArray);
            //循环图片对象列表，求距离并计入数组
            for (let i = 0; i < imageNearArray.length; i++) {
                // 所有图片块透明度回复
                this.setOpacity(this.imageArray[i], 100);
                // 排除自身和正在移动的对象
                if (imageNearArray[i] != dragTarget && !imageNearArray[i].isMove) {
                    // 中心点距离
                    let item = this.getDistance(dragTarget, imageNearArray[i]);
                    // 获取距离最小对象
                    if (disArray.length == 0 || (item < disArray[1])) {
                        disArray = [imageNearArray[i], item];
                    }
                }
            }
            // 返回距离最小对象透明度
            this.setOpacity(disArray[0], 50);
            // 返回距离最小对象
            return disArray[0];
        }
        // 求两对象中心两点之间的距离,传入对象,利用勾股定理求值
        getDistance(origin, target) {
            let a = (origin.offsetLeft + origin.offsetWidth / 2) - (target.offsetLeft + target.offsetWidth / 2);
            let b = (origin.offsetTop + origin.offsetHeight / 2) - (target.offsetTop + target.offsetHeight / 2);
            return Math.ceil(Math.sqrt(a * a + b * b));
        }
        // 设置模糊程度
        setOpacity(node, level) {
            node.style.opacity = level / 100;
        }
        // 随机排序
        rank() {
            let imageRankArray = $.extend([], true, [], this.imageArray);
            let originRankArray = $.extend([], true, [], this.originArray);
            while (imageRankArray.length > 0) {
                // 随机获取ID
                let ranLi = imageRankArray.splice(this.random(0, imageRankArray.length), 1);
                // 随机获取位置,并从原数组移除，返回移除项组成的数组
                let ranCss = originRankArray.splice(this.random(0, originRankArray.length), 1);
                // 赋值随机位置
                let type = imageRankArray.length == 0 ? 0 : 2;
                this.startMove(ranLi[0], ranCss[0][1], ranCss[0][2], this.opts.createDuration, type);
            }
        }
        // 返回随机数函数
        random(from, to) {
            return Math.floor(Math.random() * (to - from) + from);
        }
        // 设置移动 type=1 交换位置/type=0 首次生成
        startMove(target, x, y, time, type) {
            let that = this;
            //判断对象是否移动
            target.isMove = true;
            $(target).animate({
                "left": x,
                "top": y
            }, time, 'swing', function () {
                target.isMove = false;
                if (that.check()) {
                    // 拼图成功
                    if (type === 1) {
                        // 延迟，保证动画执行完毕
                        setTimeout(function () {
                            that.opts.callback && that.opts.callback();
                        }, 100)
                    }
                    // 首次生成拼图与原图相同，重新生成
                    if (type === 0) {
                        that.rank();
                    }
                }
            })
        }
        // 检查拼图是否完成
        check() {
            for (let j = 0; j < this.originArray.length; j++) {
                let loopLi = $('#pt_game' + this.originArray[j][0])[0];
                if (parseInt(loopLi.style.left) != this.originArray[j][1] || parseInt(loopLi.style.top) != this.originArray[j][2]) {
                    return false;
                }
            }
            return true;
        }
    }
    $.fn.pinTu = function (options) {
        let builder = new Builder(this, options);
        return builder.init();;
    }
})(jQuery);
